package dao

import (
	"errors"
	"fmt"
	"strings"

	"gostore/config"
	_ "gostore/config"
	"gostore/util/log"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var db *gorm.DB

//打开数据库链接
func Open(dbCfg config.DBConfig) *gorm.DB {
	l := log.Factory(Open)
	l.Info("config database")

	dbSF := "%s:%s@tcp(%s:%s)/%s?charset=utf8"
	dbUrl := fmt.Sprintf(dbSF, dbCfg.Username, dbCfg.Password, dbCfg.Host, dbCfg.Port, dbCfg.DBName)
	dbType := dbCfg.Type

	l.Info("connecting db:", dbUrl)
	db0, err := gorm.Open(dbType, dbUrl)
	if err != nil {
		l.Fatal("database connect fail: ", err)
	}

	db0.SingularTable(true)
	db0.LogMode(true)
	db0.DB().SetMaxIdleConns(10)

	db = db0
	return db
}

func GetDB() *gorm.DB {
	l := log.Factory(GetDB)

	if db == nil {
		l.Fatal("database need initialization")
	}
	return db
}

type DaoT struct {
	TableName string
}

func (d DaoT) Save(entity interface{}) error {
	isBlank := GetDB().NewRecord(entity)
	if isBlank {
		return db.Create(entity).Error
	}
	return nil
}

func (d DaoT) GetById(entity interface{}, id int) error {
	return GetDB().Where("id = ?", id).First(entity).Error
}

func (d DaoT) List(entity interface{}, page int, limit int) (count int64, err error) {
	return d.QueryList(entity, page, limit, "", "")
}

// @Todo
// @Todo rawOrder base64 加解密
func (d DaoT) QueryList(entity interface{}, page int, limit int, rawQuery, rawOrder string) (count int64, err error) {
	db := GetDB()
	db, err = buildWhere(rawQuery, db)
	if err != nil {
		return
	}
	db, err = buildOrder(rawOrder, db)
	if err != nil {
		return
	}

	db.Offset(PageToOffset(page, limit)).
		Limit(limit).
		Find(entity).
		Count(count)
	err = db.Error

	return
}

func (d DaoT) QueryListAll(list interface{}, rawQuery, rawOrder string) (count int64, err error) {
	db := GetDB()
	db, err = buildWhere(rawQuery, db)
	if err != nil {
		return
	}
	db, err = buildOrder(rawOrder, db)
	if err != nil {
		return
	}
	db.Find(list).Count(count)

	err = db.Error
	return
}

func buildWhere(rawQuery string, db *gorm.DB) (*gorm.DB, error) {
	if rawQuery != "" {
		querys := strings.Split(rawQuery, ",")
		for _, query := range querys {
			oneQuery := strings.Split(query, ":")
			if len(oneQuery) != 2 && len(oneQuery) != 3 {
				return db, errors.New("parseRawQuery error, rawQuery should like: 'title:=:golang,name:like:%jason%,id:<:100' , if the whereType is '=', you can omitted it: title:golang, notice: '%' after encode is %25")
			}
			if len(oneQuery) == 2 {
				field := oneQuery[0]
				whereType := "="
				value := oneQuery[1]
				db = db.Where(field+" "+whereType+" "+"?", value)
			}
			if len(oneQuery) == 3 {
				field := oneQuery[0]
				whereType := oneQuery[1]
				value := oneQuery[2]
				db = db.Where(field+" "+whereType+" "+"?", value)
			}
		}
	}
	return db, nil
}

func buildOrder(rawOrder string, db *gorm.DB) (*gorm.DB, error) {

	if rawOrder != "" {
		orders := strings.Split(rawOrder, ",")
		for _, order := range orders {
			oneOrder := strings.Split(order, ":")
			if len(oneOrder) != 1 && len(oneOrder) != 2 {
				return db, errors.New("parse rawOrder error, rawOrder should like:'created:desc,id:asc,name', orderType default is asc")
			}

			if len(oneOrder) == 1 {
				field := oneOrder[0]
				db = db.Order(field)
			}
			if len(oneOrder) == 2 {
				field := oneOrder[0]
				orderType := oneOrder[1]
				db = db.Order(field + " " + orderType)
			}
		}
	}
	return db, nil
}

// func (d DaoT) UpdateById(entity interface{}) error {

// }

//
//Delete 接收一个'id'数组'ids'，删除'id'包含在'ids'中的记录
func (d DaoT) Delete(entity interface{}, args interface{}) error {
	return GetDB().Where("id in (?)", args).Delete(entity).Error
}

//forever delete
func (d DaoT) Deletef(entity interface{}, args interface{}) error {
	return GetDB().Unscoped().Where("id in (?)", args).Delete(entity).Error
}
